package test.philosopher;

import java.util.Random;
import java.util.concurrent.Semaphore;

public class PhilosopherDinner {

	public static final int NUM_PHILOS = 5;

	public static final int CYCLE = 5;

	Philosopher[] philosophers;

	Fork[] forks;

	Random random;

	public PhilosopherDinner() {
		forks = new Fork[NUM_PHILOS];
		for (int i = 1; i <= NUM_PHILOS; i++) {
			forks[i - 1] = new Fork(i);
		}

		philosophers = new Philosopher[NUM_PHILOS];
		for (int i = 1; i <= NUM_PHILOS; i++) {
			philosophers[i - 1] = new Philosopher(i, forks[i - 1], forks[i % NUM_PHILOS]);
		}

		random = new Random();
	}


	class Fork {
		int id;
		Semaphore semaphore;

		public Fork(int id) {
			this.id = id;
			semaphore = new Semaphore(1, true);
		}

		public void take() {
			try {
				semaphore.acquire();
			} catch (InterruptedException e) {
			}
		}
		
		public void drop() {
			semaphore.release();
		}
	}

	class Philosopher extends Thread {

		int id;
		Fork leftFork;
		Fork rightFork;

		public Philosopher(int id, Fork leftFork, Fork rightFork) {
			this.id = id;
			this.leftFork = leftFork;
			this.rightFork = rightFork;
		}

		public void think() {
			try {
				// sleep for 0..10 seconds
				System.out.println("The Philosopher " + id + " is thinking now ...");
				Thread.sleep(random.nextInt(10000));
			} catch (InterruptedException e) {
			}
		}

		public void eat() {
			try {
				// eat for 0.. 5 seconds
				System.out.println("The Philosopher " + id + " is eating now ...");
				Thread.sleep(random.nextInt(5000));
			} catch (InterruptedException e) {
			}
		}

		@Override
		public void run() {
			for (int i = 0; i < CYCLE; i++) {
				think();
				
				// last one take revere order, rigth fork first
				if (id == NUM_PHILOS) {
					System.out.println("The Philosopher " + id + " try to grab right fork " + rightFork.id);
					rightFork.take();
					System.out.println("The Philosopher " + id + " took right fork " + rightFork.id);
					System.out.println("The Philosopher " + id + " try to grab left fork " + leftFork.id);
					leftFork.take();
					System.out.println("The Philosopher " + id + " took left fork " + leftFork.id);
					
					eat();
					
					leftFork.drop();
					System.out.println("The Philosopher " + id + " drop left fork " + leftFork.id);
					rightFork.drop();
					System.out.println("The Philosopher " + id + " drop right fork " + rightFork.id);
					
				} else {
					// always acquire left fork first
					System.out.println("The Philosopher " + id + " try to grab left fork " + leftFork.id);
					leftFork.take();
					System.out.println("The Philosopher " + id + " took left fork " + leftFork.id);
					System.out.println("The Philosopher " + id + " try to grab right fork " + rightFork.id);
					rightFork.take();
					System.out.println("The Philosopher " + id + " took right fork " + rightFork.id);
					
					eat();
					
					rightFork.drop();
					System.out.println("The Philosopher " + id + " drop right fork " + rightFork.id);
					leftFork.drop();
					System.out.println("The Philosopher " + id + " drop left fork " + leftFork.id);
				}
			}
		}
	}

	public static void main(String[] args) {
		PhilosopherDinner dinner = new PhilosopherDinner();
		for (Philosopher p : dinner.philosophers) {
			p.start();
		}

		try {
			for (Philosopher p : dinner.philosophers) {
				p.join();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Congratulations! The dinner is OVER.");
	}
}
